package goplugin

import (
	"bytes"
	"crypto/md5"
	"crypto/rc4"
	"crypto/sha1"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"math/big"
	"math/rand"
	"net"
	"strings"
	"time"

	"github.com/opensec-cn/kunpeng/config"
	"github.com/opensec-cn/kunpeng/plugin"
	"github.com/opensec-cn/kunpeng/util"
)

//rdp_CVE_2019_0708 ...
type rdp_CVE_2019_0708 struct {
	info   plugin.Plugin
	result []plugin.Plugin
	socket *net.TCPConn
}

func init() {
	plugin.Regist("rdp", &rdp_CVE_2019_0708{})
}

func (p *rdp_CVE_2019_0708) Init() plugin.Plugin {
	p.info = plugin.Plugin{
		Name:    "CVE-2019-0708 Microsoft Remote Desktop RCE Checker",
		Remarks: "攻击者可利用此漏洞远程执行命令, 控制计算机。该检测插件不会引起目标 DOS",
		Level:   0,
		Type:    "RCE",
		Author:  "Medicean",
		References: plugin.References{
			URL:  "https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0708",
			CVE:  "CVE-2019-0708",
			KPID: "KP-0084",
		},
	}
	return p.info
}

func (p *rdp_CVE_2019_0708) GetResult() []plugin.Plugin {
	var result = p.result
	p.result = []plugin.Plugin{}
	return result
}

func (p *rdp_CVE_2019_0708) Check(netloc string, meta plugin.TaskMeta) bool {
	client := p
	if meta.System != "" && meta.System != "windows" {
		return false
	}
	if strings.IndexAny(netloc, "http") == 0 {
		return false
	}

	remoteaddr, err := net.ResolveTCPAddr("tcp4", netloc)
	if err != nil {
		client.log("ResolveTCPAddr error: %s", err.Error())
		return false
	}
	client.socket, err = net.DialTCP("tcp4", nil, remoteaddr)
	if err != nil {
		client.log("Dial %s error: %s", remoteaddr.Network(), err.Error())
		return false
	}
	defer client.socket.Close()
	client.socket.SetNoDelay(true)
	if config.Config.Timeout < 10 {
		client.socket.SetDeadline(time.Now().Add(time.Second * time.Duration(10)))
	} else {
		client.socket.SetDeadline(time.Now().Add(time.Second * time.Duration(config.Config.Timeout)))
	}
	isrdp, err := client.check_rdp()
	if err != nil {
		client.log("check_rdp error: %s", err.Error())
		return false
	}
	if !isrdp {
		client.log("check_rdp fail: Could not connect to RDP.")
		return false
	}

	//send initial client data
	pduConnectInitial := "\x03\x00\x01\xca\x02\xf0\x80\x7f\x65\x82\x01\xbe\x04\x01\x01\x04\x01\x01\x01\x01\xff\x30\x20\x02\x02\x00\x22\x02\x02\x00\x02\x02\x02\x00\x00\x02\x02\x00\x01\x02\x02\x00\x00\x02\x02\x00\x01\x02\x02\xff\xff\x02\x02\x00\x02\x30\x20\x02\x02\x00\x01\x02\x02\x00\x01\x02\x02\x00\x01\x02\x02\x00\x01\x02\x02\x00\x00\x02\x02\x00\x01\x02\x02\x04\x20\x02\x02\x00\x02\x30\x20\x02\x02\xff\xff\x02\x02\xfc\x17\x02\x02\xff\xff\x02\x02\x00\x01\x02\x02\x00\x00\x02\x02\x00\x01\x02\x02\xff\xff\x02\x02\x00\x02\x04\x82\x01\x4b\x00\x05\x00\x14\x7c\x00\x01\x81\x42\x00\x08\x00\x10\x00\x01\xc0\x00\x44\x75\x63\x61\x81\x34\x01\xc0\xd8\x00\x04\x00\x08\x00\x20\x03\x58\x02\x01\xca\x03\xaa\x09\x04\x00\x00\x28\x0a\x00\x00"
	pduConnectInitial += "\x78\x00\x31\x00\x38\x00\x31\x00\x30\x00"
	pduConnectInitial += "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xca\x01\x00\x00\x00\x00\x00\x18\x00\x07\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xc0\x0c\x00\x09\x00\x00\x00\x00\x00\x00\x00\x02\xc0\x0c\x00\x03\x00\x00\x00\x00\x00\x00\x00\x03\xc0\x44\x00\x05\x00\x00\x00\x63\x6c\x69\x70\x72\x64\x72\x00\xc0\xa0\x00\x00\x4d\x53\x5f\x54\x31\x32\x30\x00\x80\x80\x00\x00\x72\x64\x70\x73\x6e\x64\x00\x00\xc0\x00\x00\x00\x73\x6e\x64\x64\x62\x67\x00\x00\xc0\x00\x00\x00\x72\x64\x70\x64\x72\x00\x00\x00\x80\x80\x00\x00"
	client.log("pdu_connect_initial...")
	res, err := client.rdp_send_recv([]byte(pduConnectInitial))
	if err != nil {
		client.log("pdu_connect_initial error: %s", err.Error())
		return false
	}
	rsmod, rsexp, rsran, serverRand, bitlen, err := client.rdp_parse_serverdata(res)
	if err != nil {
		client.log("Parse PDU Initial Data error: %s", err.Error())
		return false
	}
	client.log("rsmod: %s", rsmod)
	client.log("rsexp: %s", rsexp)
	client.log("rsran: %s", rsran)

	// erect domain and attach user
	pduErectDomainRequest := "\x03\x00\x00\x0c\x02\xf0\x80\x04\x00\x01\x00\x01"
	err = client.rdp_send([]byte(pduErectDomainRequest))
	if err != nil {
		client.log("send pduErectDomainRequest error: %s", err.Error())
		return false
	}
	pduAttachUserRequest := "\x03\x00" + // header
		"\x00\x08" + // length
		"\x02\xf0\x80" + // X.224 Data TPDU (2 bytes: 0xf0 = Data TPDU, 0x80 = EOT, end of transmission)
		"\x28" // PER encoded PDU contents

	res, err = client.rdp_send_recv([]byte(pduAttachUserRequest))
	if err != nil {
		client.log("send pduAttachUserRequest error: %s", err.Error())
		return false
	}

	user1 := int(client.bytes_to_bignum(res[9:], "big").Int64())
	client.log("User1: %d", user1)

	// send channel requests
	client.rdp_send_recv([]byte(client.pdu_channel_request(user1, 1009)))
	client.rdp_send_recv([]byte(client.pdu_channel_request(user1, 1003)))
	client.rdp_send_recv([]byte(client.pdu_channel_request(user1, 1004)))
	client.rdp_send_recv([]byte(client.pdu_channel_request(user1, 1005)))
	client.rdp_send_recv([]byte(client.pdu_channel_request(user1, 1006)))
	client.rdp_send_recv([]byte(client.pdu_channel_request(user1, 1007)))
	client.rdp_send_recv([]byte(client.pdu_channel_request(user1, 1008)))

	clientRand := strings.Repeat("\x41", 32)
	rcran := client.bytes_to_bignum([]byte(clientRand), "little")
	client.log("rcran: %v", rcran)

	client.log("Sending security exchange PDU")
	err = client.rdp_send([]byte(client.pdu_security_exchange(rcran, rsexp, rsmod, bitlen)))
	if err != nil {
		client.log("Sending security exchange PDU error: %s", err.Error())
		return false
	}
	rc4encstart, rc4decstart, hmackey, sessblob := client.rdp_calculate_rc4_keys([]byte(clientRand), serverRand)
	client.log("RC4_ENC_KEY: %s", hex.EncodeToString(rc4encstart))
	client.log("RC4_DEC_KEY: %s", hex.EncodeToString(rc4decstart))
	client.log("HMAC_KEY: %s", hex.EncodeToString(hmackey))
	client.log("SESS_BLOB: %s", hex.EncodeToString(sessblob))

	rc4enckey, _ := rc4.NewCipher(rc4encstart)
	client.log("Sending encrypted client info PDU")
	pduClientInfo := "\x00\x00\x00\x00\x33\x01\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00"
	pduClientInfo += "\x75\x00\x73\x00\x65\x00\x72\x00\x30\x00"
	pduClientInfo += "\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x1c\x00"
	pduClientInfo += "\x31\x00\x39\x00\x32\x00\x2e\x00\x31\x00\x36\x00\x38\x00\x2e\x00\x31\x00\x2e\x00\x32\x00\x30\x00\x38\x00"
	pduClientInfo += "\x00\x00\x3c\x00\x43\x00\x3a\x00\x5c\x00\x57\x00\x49\x00\x4e\x00\x4e\x00\x54\x00\x5c\x00\x53\x00\x79\x00\x73\x00\x74\x00\x65\x00\x6d\x00\x33\x00\x32\x00\x5c\x00\x6d\x00\x73\x00\x74\x00\x73\x00\x63\x00\x61\x00\x78\x00\x2e\x00\x64\x00\x6c\x00\x6c\x00\x00\x00\xa4\x01\x00\x00\x47\x00\x54\x00\x42\x00\x2c\x00\x20\x00\x6e\x00\x6f\x00\x72\x00\x6d\x00\x61\x00\x6c\x00\x74\x00\x69\x00\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x05\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x47\x00\x54\x00\x42\x00\x2c\x00\x20\x00\x73\x00\x6f\x00\x6d\x00\x6d\x00\x61\x00\x72\x00\x74\x00\x69\x00\x64\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x05\x00\x02\x00\x00\x00\x00\x00\x00\x00\xc4\xff\xff\xff\x00\x00\x00\x00\x27\x00\x00\x00\x00\x00"

	pduClientInfo_pkt := client.rdp_encrypted_pkt(pduClientInfo, rc4enckey, hmackey, "\x48\x00", "\x00\x00", "\x03\xeb")
	res, err = client.rdp_send_recv(pduClientInfo_pkt)
	if err != nil {
		client.log("Sending encrypted client info PDU error: %s", err.Error())
		return false
	}
	client.log("Received License packet: %s", hex.EncodeToString(res))

	res, err = client.rdp_recv()
	if err != nil {
		client.log("Received Server Demand packet error: %s", err.Error())
		return false
	}
	client.log("Received Server Demand packet: %s", hex.EncodeToString(res))

	client.log("Sending client confirm active PDU")
	pduClientConfirmActive := "\xa4\x01\x13\x00\xf1\x03\xea\x03\x01\x00\xea\x03\x06\x00\x8e\x01\x4d\x53\x54\x53\x43\x00\x0e\x00\x00\x00\x01\x00\x18\x00\x01\x00\x03\x00\x00\x02\x00\x00\x00\x00\x0d\x04\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x1c\x00\x10\x00\x01\x00\x01\x00\x01\x00\x20\x03\x58\x02\x00\x00\x01\x00\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x58\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x14\x00\x00\x00\x01\x00\x47\x01\x2a\x00\x01\x01\x01\x01\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x01\x00\x00\x00\x00\x00\x01\x01\x01\x00\x00\x01\x01\x01\x00\x00\x00\x00\xa1\x06\x00\x00\x00\x00\x00\x00\x00\x84\x03\x00\x00\x00\x00\x00\xe4\x04\x00\x00\x13\x00\x28\x00\x00\x00\x00\x03\x78\x00\x00\x00\x78\x00\x00\x00\x50\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x0a\x00\x01\x00\x14\x00\x14\x00\x0a\x00\x08\x00\x06\x00\x00\x00\x07\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x0c\x00\x00\x00\x00\x00\x02\x00\x02\x00\x09\x00\x08\x00\x00\x00\x00\x00\x0f\x00\x08\x00\x01\x00\x00\x00\x0d\x00\x58\x00\x01\x00\x00\x00\x09\x04\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x08\x00\x01\x00\x00\x00\x0e\x00\x08\x00\x01\x00\x00\x00\x10\x00\x34\x00\xfe\x00\x04\x00\xfe\x00\x04\x00\xfe\x00\x08\x00\xfe\x00\x08\x00\xfe\x00\x10\x00\xfe\x00\x20\x00\xfe\x00\x40\x00\xfe\x00\x80\x00\xfe\x00\x00\x01\x40\x00\x00\x08\x00\x01\x00\x01\x02\x00\x00\x00"

	client.rdp_send(client.rdp_encrypted_pkt(pduClientConfirmActive, rc4enckey, hmackey, "\x38\x00", "\x00\x00", "\x03\xeb"))

	client.log("Sending client synchronize PDU")
	client.log("Sending client control cooperate PDU")
	synch := client.rdp_encrypted_pkt("\x16\x00\x17\x00\xf1\x03\xea\x03\x01\x00\x00\x01\x08\x00\x1f\x00\x00\x00\x01\x00\xea\x03", rc4enckey, hmackey, "\x08\x00", "\x00\x00", "\x03\xeb")
	coop := client.rdp_encrypted_pkt("\x1a\x00\x17\x00\xf1\x03\xea\x03\x01\x00\x00\x01\x0c\x00\x14\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00", rc4enckey, hmackey, "\x08\x00", "\x00\x00", "\x03\xeb")
	client.log("Grea2t!")
	client.rdp_send(client.BytesCombine(synch, coop))

	client.log("Sending client control request control PDU")
	client.rdp_send(client.rdp_encrypted_pkt("\x1a\x00\x17\x00\xf1\x03\xea\x03\x01\x00\x00\x01\x0c\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00", rc4enckey, hmackey, "\x08\x00", "\x00\x00", "\x03\xeb"))

	client.log("Sending client persistent key list PDU")
	pduClientPersistentKeyList := "\x49\x03\x17\x00\xf1\x03\xea\x03\x01\x00\x00\x01\x3b\x03\x1c\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
	pduClientPersistentKeyList += strings.Repeat("\xaa", 807)
	client.rdp_send(client.rdp_encrypted_pkt(pduClientPersistentKeyList, rc4enckey, hmackey, "\x08\x00", "\x00\x00", "\x03\xeb"))

	client.log("Sending client font list PDU")
	client.rdp_send(client.rdp_encrypted_pkt("\x1a\x00\x17\x00\xf1\x03\xea\x03\x01\x00\x00\x01\x0c\x00\x27\x00\x00\x00\x00\x00\x00\x00\x03\x00\x32\x00", rc4enckey, hmackey, "\x08\x00", "\x00\x00", "\x03\xeb"))

	result := client.try_check(rc4enckey, hmackey)
	if result {
		client.log("Vulnerable")
		return true
	} else {
		client.log("Not Vulnerable")
		return false
	}
}

func (p *rdp_CVE_2019_0708) try_check(rc4enckey *rc4.Cipher, hmackey []byte) bool {
	var res, pkt []byte
	var err error
	for i := 0; i < 6; i++ {
		res, _ = p.rdp_recv()
	}

	for i := 0; i < 6; i++ {
		// x86
		pkt = p.rdp_encrypted_pkt("\x10\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", rc4enckey, hmackey, "\x08\x00", "\x00\x00", "\x03\xed")
		p.rdp_send(pkt)
		// x64
		pkt = p.rdp_encrypted_pkt("\x20\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", rc4enckey, hmackey, "\x08\x00", "\x00\x00", "\x03\xed")
		p.rdp_send(pkt)
		for j := 0; j < 4; j++ {
			res, err = p.rdp_recv()
			if err != nil {
				p.log("Check Recv Error: %s", err.Error())
				continue
			}
			p.log("Check Recv: %s", hex.EncodeToString(res))
			if bytes.Contains(res, []byte("\x03\x00\x00\x09\x02\xf0\x80\x21\x80")) {
				p.result = append(p.result, p.info)
				return true
			}
		}
	}
	return false
}

//log 输出 debug 信息
func (p rdp_CVE_2019_0708) log(format string, a ...interface{}) {
	util.Logger.Warning(fmt.Sprintf(format, a...))
}

func (p *rdp_CVE_2019_0708) rdp_send(data []byte) error {
	_, err := p.socket.Write(data)
	if err != nil {
		return err
	}
	return nil
}

func (p *rdp_CVE_2019_0708) rdp_recv() ([]byte, error) {
	data := make([]byte, 8192)
	// 这里其实可以先读 TPTK 头部, 再根据 Length 读数据
	n, err := p.socket.Read(data)
	if err != nil {
		return nil, err
	}
	return data[:n], nil
}

func (p *rdp_CVE_2019_0708) rdp_send_recv(data []byte) (ret []byte, err error) {
	err = p.rdp_send(data)
	if err != nil {
		return
	}
	ret, err = p.rdp_recv()
	return
}

//check_rdp 检测是否是 rdp 协议
func (p *rdp_CVE_2019_0708) check_rdp() (bool, error) {
	p.log("Verifying RDP protocol...")
	pkt := "\x03\x00\x00\x2b"
	pkt += "\x26\xe0\x00\x00\x00\x00\x00\x43\x6f\x6f\x6b\x69\x65\x3a\x20\x6d\x73\x74\x73\x68\x61\x73\x68\x3d"
	pkt += p.rand_text(5)
	pkt += "\x0d\x0a\x01\x00\x08\x00\x00\x00\x00\x00"

	n, err := p.socket.Write([]byte(pkt))
	if err != nil {
		return false, err
	}
	p.log("CheckRDP: Send %d Bytes", n)
	data := make([]byte, 1024)
	n, err = p.socket.Read(data)
	if err != nil {
		return false, err
	}
	p.log("Recv %d Bytes: %q", n, data[:n])
	// TKPK v3 协议头
	// Version 3, Reserved: 0, Length: 19 (0x0013)
	if bytes.Equal(data[:4], []byte{0x03, 0x00, 0x00, 0x13}) {
		return true, nil
	}
	return false, nil
}

func (p *rdp_CVE_2019_0708) rdp_parse_serverdata(pkt []byte) (*big.Int, *big.Int, *big.Int, []byte, int, error) {
	// p.log("SERVER_DATA: %s", hex.EncodeToString(pkt))
	var modulus, publicExponent, serverRandom []byte
	var bitlen int
	ptr := 0
	rdp_pkt := pkt[0x49:]
	for ptr < len(rdp_pkt) {
		header_type := rdp_pkt[ptr : ptr+2]
		// 网络字节顺序(大Endian)的unsigned short (16bit 无符号整数)
		header_length := p.bytes_to_bignum(rdp_pkt[ptr+2:ptr+4], "little")
		p.log("header: %s len %d", hex.EncodeToString(header_type), header_length)
		if string(header_type) == "\x02\x0c" {
			p.log("security header")
			serverRandom = rdp_pkt[ptr+20 : ptr+52]
			publicExponent = rdp_pkt[ptr+84 : ptr+88]
			modulus = rdp_pkt[ptr+88 : ptr+152]
			p.log("modulus_old %s", hex.EncodeToString(modulus))
			rsa_magic := string(rdp_pkt[ptr+68 : ptr+72])
			p.log("RSA magic: %s", rsa_magic)
			if rsa_magic != "RSA1" {
				return nil, nil, nil, nil, 0, errors.New("Server cert isn't RSA, this scenario isn't supported (yet).")
			}
			bitlen = p.Bytes4ToInt(rdp_pkt[ptr+72:ptr+76], "big") - 8
			p.log("RSA bitlen: %d", bitlen)
			modulus = rdp_pkt[ptr+88 : ptr+88+bitlen]
			p.log("modulus_new %s", hex.EncodeToString(modulus))
		}
		ptr += int(header_length.Int64())
	}
	// serverRandom := pkt[121+4 : 153+4]
	// publicExponent := pkt[185+4 : 189+4]
	// modulus := pkt[189+4 : 253+4]
	p.log("SERVER_MODULUS: %s", hex.EncodeToString(modulus))
	p.log("SERVER_EXPONENT: %s", hex.EncodeToString(publicExponent))
	p.log("SERVER_RANDOM: %s", hex.EncodeToString(serverRandom))
	rsmod := p.bytes_to_bignum(modulus, "little")
	rsexp := p.bytes_to_bignum(publicExponent, "little")
	rsran := p.bytes_to_bignum(serverRandom, "little")
	serverRandom = p.reverseByte(serverRandom)
	return rsmod, rsexp, rsran, serverRandom, bitlen, nil
}

// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/9cde84cd-5055-475a-ac8b-704db419b66f
func (p *rdp_CVE_2019_0708) pdu_security_exchange(rcran, rsexp, rsmod *big.Int, bitlen int) string {
	// rsa_encrypt
	rcran.Exp(rcran, rsexp, nil)
	rcran.Mod(rcran, rsmod)
	encrypted_rcran_bignum := rcran
	// encrypted_rcran_bignum := p.intpow(rcran, rsexp) % rsmod
	encrypted_rcran := p.reverseByte(encrypted_rcran_bignum.Bytes())

	p.log("Encrypted client random: %s", hex.EncodeToString(encrypted_rcran))

	bitlen += 8
	bitlen_hex := p.IntToBytes4(bitlen, "little")
	userdata_length := 8 + bitlen
	userdata_length_low := userdata_length & 0xFF
	userdata_length_high := userdata_length / 256
	flags := 0x80 | userdata_length_high

	pkt := "\x03\x00"
	pkt += string(p.IntToBytes2(userdata_length+15, "big")) // TPKT Length
	pkt += "\x02\xf0\x80"                                   // X.224
	pkt += "\x64"                                           // sendDataRequest
	pkt += "\x00\x08"                                       // intiator userId
	pkt += "\x03\xeb"                                       // channelId = 1003
	pkt += "\x70"                                           // dataPriority
	pkt += string(p.IntToBytes(flags))                      //
	pkt += string(p.IntToBytes(userdata_length_low))        // UserData length
	pkt += "\x01\x00"                                       // securityHeader flags
	pkt += "\x00\x00"                                       // securityHeader flagsHi
	pkt += string(bitlen_hex)                               // securityPkt length
	pkt += string(encrypted_rcran)                          // 64 bytes encrypted client random
	pkt += "\x00\x00\x00\x00\x00\x00\x00\x00"               // 8 bytes rear padding (always present)

	p.log("pdu_security_exchange: %s", hex.EncodeToString([]byte(pkt)))

	return pkt
}

func (p *rdp_CVE_2019_0708) pdu_channel_request(user1, channel_id int) string {
	pkt := ""
	pkt += "\x03\x00"                               // header
	pkt += "\x00\x0c"                               // length
	pkt += "\x02\xf0\x80"                           // X.224
	pkt += "\x38"                                   // ChannelJoin request
	pkt += string(p.IntToBytes2(user1, "big"))      //rb: [user1].pack("n")
	pkt += string(p.IntToBytes2(channel_id, "big")) //rb: [channel_id].pack("n")
	return pkt
}

// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/705f9542-b0e3-48be-b9a5-cf2ee582607f
// SaltedHash(S, I) = MD5(S + SHA(I + S + ClientRandom + ServerRandom))
func (p *rdp_CVE_2019_0708) rdp_salted_hash(s_bytes, i_bytes, clientRandom_bytes, serverRandom_bytes []byte) []byte {
	sha1Ctx := sha1.New()
	sha1Ctx.Write(p.BytesCombine(i_bytes, s_bytes, clientRandom_bytes, serverRandom_bytes))
	sha1_bytes := sha1Ctx.Sum(nil)
	md5Ctx := md5.New()
	md5Ctx.Write(p.BytesCombine(s_bytes, sha1_bytes))
	md5_bytes := md5Ctx.Sum(nil)
	return md5_bytes
}

// FinalHash(K) = MD5(K + ClientRandom + ServerRandom)
func (p *rdp_CVE_2019_0708) rdp_final_hash(k, clientRandom_bytes, serverRandom_bytes []byte) []byte {
	md5Ctx := md5.New()
	md5Ctx.Write(p.BytesCombine(k, clientRandom_bytes, serverRandom_bytes))
	md5_bytes := md5Ctx.Sum(nil)
	return md5_bytes
}

func (p *rdp_CVE_2019_0708) rdp_calculate_rc4_keys(clientRandom, serverRandom []byte) ([]byte, []byte, []byte, []byte) {
	//preMasterSecret = First192Bits(ClientRandom) + First192Bits(ServerRandom)
	preMasterSecret := p.BytesCombine(clientRandom[0:24], serverRandom[0:24])

	// PreMasterHash(I) = SaltedHash(preMasterSecret, I)
	// MasterSecret = PreMasterHash(0x41) + PreMasterHash(0x4242) + PreMasterHash(0x434343)
	masterSecret := p.BytesCombine(p.rdp_salted_hash(preMasterSecret, []byte("A"), clientRandom, serverRandom), p.rdp_salted_hash(preMasterSecret, []byte("BB"), clientRandom, serverRandom), p.rdp_salted_hash(preMasterSecret, []byte("CCC"), clientRandom, serverRandom))

	// MasterHash(I) = SaltedHash(MasterSecret, I)
	// SessionKeyBlob = MasterHash(0x58) + MasterHash(0x5959) + MasterHash(0x5A5A5A)
	sessionKeyBlob := p.BytesCombine(p.rdp_salted_hash(masterSecret, []byte("X"), clientRandom, serverRandom), p.rdp_salted_hash(masterSecret, []byte("YY"), clientRandom, serverRandom), p.rdp_salted_hash(masterSecret, []byte("ZZZ"), clientRandom, serverRandom))

	// InitialClientDecryptKey128 = FinalHash(Second128Bits(SessionKeyBlob))
	initialClientDecryptKey128 := p.rdp_final_hash(sessionKeyBlob[16:32], clientRandom, serverRandom)

	// InitialClientEncryptKey128 = FinalHash(Third128Bits(SessionKeyBlob))
	initialClientEncryptKey128 := p.rdp_final_hash(sessionKeyBlob[32:48], clientRandom, serverRandom)

	macKey := sessionKeyBlob[0:16]

	p.log("preMasterSecret: %s", hex.EncodeToString(preMasterSecret))
	p.log("masterSecret: %s", hex.EncodeToString(masterSecret))
	p.log("sessionKeyBlob: %s", hex.EncodeToString(sessionKeyBlob))
	p.log("macKey: %s", hex.EncodeToString(macKey))
	p.log("initialClientDecryptKey128: %s", hex.EncodeToString(initialClientDecryptKey128))
	p.log("initialClientEncryptKey128: %s", hex.EncodeToString(initialClientEncryptKey128))
	return initialClientEncryptKey128, initialClientDecryptKey128, macKey, sessionKeyBlob
}

// default flags = "\x08\x00", flagsHi = "\x00\x00", channelId="\x03\xeb"
func (p *rdp_CVE_2019_0708) rdp_encrypted_pkt(data string, rc4enckey *rc4.Cipher, hmackey []byte, flags, flagsHi, channelId string) []byte {
	userData_len := len(data) + 12
	udl_with_flag := 0x8000 | userData_len
	pkt := "\x02\xf0\x80" // X.224
	pkt += "\x64"         // sendDataRequest
	pkt += "\x00\x08"     // intiator userId .. TODO: for a functional client this isn't static
	pkt += channelId      // channelId = 1003
	pkt += "\x70"         // dataPriority
	//pkt += "\x80" // TODO: half of this is length field ......
	pkt += string(p.IntToBytes2(udl_with_flag, "big"))
	pkt += flags   //{}"\x48\x00" // flags  SEC_INFO_PKT | SEC_ENCRYPT
	pkt += flagsHi // flagsHi
	pkt += string(p.rdp_hmac(hmackey, data)[0:8])
	pkt += string(p.rdp_rc4_crypt(rc4enckey, data))

	tpkt := "\x03\x00"
	tpkt += string(p.IntToBytes2(len(pkt)+4, "big"))
	tpkt += pkt
	return []byte(tpkt)
}

// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/7c61b54e-f6cd-4819-a59a-daf200f6bf94
// mac_salt_key = "W\x13\xc58\x7f\xeb\xa9\x10*\x1e\xddV\x96\x8b[d"
// data_content = "\x12\x00\x17\x00\xef\x03\xea\x03\x02\x00\x00\x01\x04\x00$\x00\x00\x00"
// hmac = rdp_hmac(mac_salt_key, data_content) # == hexlified: "22d5aeb486994a0c785dc929a2855923"
func (p *rdp_CVE_2019_0708) rdp_hmac(mac_salt_key []byte, data string) []byte {
	sha1Ctx := sha1.New()
	pad1 := strings.Repeat("\x36", 40)
	pad2 := strings.Repeat("\x5c", 48)

	sha1Ctx.Write(p.BytesCombine(mac_salt_key, []byte(pad1), p.IntToBytes4(len(data), "little"), []byte(data)))
	// sha1Ctx.Write(mac_salt_key)
	// sha1Ctx.Write([]byte(pad1))
	// sha1Ctx.Write(p.IntToBytes4(len([]byte(data)), "little"))
	// sha1Ctx.Write([]byte(data))
	sha1_bytes := sha1Ctx.Sum(nil)

	md5Ctx := md5.New()
	md5Ctx.Write(p.BytesCombine(mac_salt_key, []byte(pad2), sha1_bytes))
	md5_bytes := md5Ctx.Sum(nil)
	return md5_bytes
}
func (p *rdp_CVE_2019_0708) rdp_rc4_crypt(rc4obj *rc4.Cipher, data string) []byte {
	src := []byte(data)
	dst := []byte(data)
	rc4obj.XORKeyStream(dst, src)
	return dst
}

func (p *rdp_CVE_2019_0708) reverseString(s string) string {
	runes := []rune(s)
	for from, to := 0, len(runes)-1; from < to; from, to = from+1, to-1 {
		runes[from], runes[to] = runes[to], runes[from]
	}
	return string(runes)
}

func (p *rdp_CVE_2019_0708) reverseByte(in []byte) []byte {
	src := in
	for from, to := 0, len(src)-1; from < to; from, to = from+1, to-1 {
		src[from], src[to] = in[to], in[from]
	}
	return src
}

//rand_text 生成指定长度的随机字符串
func (p *rdp_CVE_2019_0708) rand_text(length int) string {
	charset := "abcdefghijklmnopqrstuvwxyz" +
		"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
	if length <= 0 {
		length = 5
	}
	b := make([]byte, length)
	for i := range b {
		b[i] = charset[seededRand.Intn(len(charset))]
	}
	return string(b)
}

// 求整数 n 次方
func (p *rdp_CVE_2019_0708) intpow(x, n int) int {
	ret := 1 // 结果初始为0次方的值，整数0次方为1。如果是矩阵，则为单元矩阵。
	for n != 0 {
		if n%2 != 0 {
			ret = ret * x
		}
		n /= 2
		x = x * x
	}
	return ret
}

// 字节转 Big.Int
//  src := []byte("\xac\x01\xba\x02")
// 	fmt.Println(bytes_to_bignum(src, "big")) => 0xac01ba02 => 2885794306
// 	fmt.Println(bytes_to_bignum(src, "little")) => 0x02ba01ac => 45744556
func (p *rdp_CVE_2019_0708) bytes_to_bignum(in []byte, order string) *big.Int {
	src := in
	if order == "little" {
		src = p.reverseByte(in)
	}
	bi := big.NewInt(0)
	bi.SetBytes(src)
	return bi
}

func (p *rdp_CVE_2019_0708) IntToBytes(n int) []byte {
	x := int8(n)
	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	return bytesBuffer.Bytes()
}

/*IntToBytes2 整形转换成2字节
IntToBytes2(8, "big") => \x00\x08
IntToBytes2(8, "little") => \x08\x00
*/
func (p *rdp_CVE_2019_0708) IntToBytes2(n int, order string) []byte {
	m := int16(n)
	bytesBuffer := bytes.NewBuffer([]byte{})
	if order == "little" {
		binary.Write(bytesBuffer, binary.LittleEndian, m)
	} else {
		binary.Write(bytesBuffer, binary.BigEndian, m)
	}

	gbyte := bytesBuffer.Bytes()

	return gbyte
}

/*IntToBytes 整形转换成4字节
IntToBytes4(259, "big") => \x00\x00\x01\x03
IntToBytes4(259, "little") => \x03\x01\x00\x00
*/
func (p *rdp_CVE_2019_0708) IntToBytes4(n int, order string) []byte {
	m := int32(n)
	bytesBuffer := bytes.NewBuffer([]byte{})
	if order == "little" {
		binary.Write(bytesBuffer, binary.LittleEndian, m)
	} else {
		binary.Write(bytesBuffer, binary.BigEndian, m)
	}

	gbyte := bytesBuffer.Bytes()

	return gbyte
}

//Bytes4ToInt 4个字节转换成整形
func (p *rdp_CVE_2019_0708) Bytes4ToInt(b []byte, order string) int {
	xx := make([]byte, 4)
	if len(b) == 2 {
		xx = []byte{b[0], b[1], 0, 0}
	} else {
		xx = b
	}

	m := len(xx)
	nb := make([]byte, 4)
	for i := 0; i < 4; i++ {
		nb[i] = xx[m-i-1]
	}
	bytesBuffer := bytes.NewBuffer(nb)

	var x int32
	if order == "little" {
		binary.Read(bytesBuffer, binary.LittleEndian, &x)
	} else {
		binary.Read(bytesBuffer, binary.BigEndian, &x)
	}
	return int(x)
}

func (p *rdp_CVE_2019_0708) BytesCombine(pBytes ...[]byte) []byte {
	return bytes.Join(pBytes, []byte(""))
}
